//: -version=HTTPPARSER_TEST http_parser.obj -debug \@tango2

module httparser ;

alias ubyte uv_uint8;
alias byte uv_int8;
alias ushort uv_uint16;
alias short uv_int16;
alias uint uv_uint32;
alias int uv_int32;
alias ulong uv_uint64;
alias long uv_int64;
alias char* uv_charptr;

extern(C) :
alias uv_int32 function(http_parser*, uv_charptr at, size_t length) http_data_cb;
alias uv_int32 function(http_parser*) http_cb;

enum http_method {
	DELETE	= 0 ,
	GET	= 1 ,
	HEAD	= 2 ,
	POST	= 3 ,
	PUT	= 4 ,
	  /* pathological */  
	CONNECT	= 5 ,
	OPTIONS	= 6 ,
	TRACE	= 7 ,
	/* webdav */
	COPY	= 8 ,
	LOCK	= 9 , 
	MKCOL = 10 ,
	MOVE = 11 ,
	PROPFIND = 12 ,
	PROPPATCH = 13 ,
	SEARCH = 14 ,
	UNLOCK = 15 ,
	/* subversion */
	REPORT = 16 ,
	MKACTIVITY = 17 ,
	CHECKOUT = 18 ,
	MERGE = 19 ,
	/* upnp */
	MSEARCH	= 20 ,
	NOTIFY = 21 ,
	SUBSCRIBE = 22 ,
	UNSUBSCRIBE = 23 ,
	/* RFC-5789 */
	PATCH = 24 ,
	PURGE = 25 ,
};

enum http_parser_type {
	HTTP_REQUEST,
	HTTP_RESPONSE,
	HTTP_BOTH 
};

enum flags{
	F_CHUNKED               = 1 << 0 ,
	F_CONNECTION_KEEP_ALIVE = 1 << 1 ,
	F_CONNECTION_CLOSE      = 1 << 2 ,
	F_TRAILING              = 1 << 3 ,
	F_UPGRADE               = 1 << 4 ,
	F_SKIPBODY              = 1 << 5
};

enum http_errno {
	 /* No error */
	OK , // "success"
	/* Callback-related errors */
	CB_message_begin , // "the on_message_begin callback failed"
	CB_status_complete , //   "the on_status_complete callback failed"
	CB_url , //  "the on_url callback failed") 
	CB_header_field , // "the on_header_field callback failed"
	CB_header_value ,  // "the on_header_value callback failed"
	CB_headers_complete , // "the on_headers_complete callback failed"
	CB_body , // "the on_body callback failed"                 
	CB_message_complete , // "the on_message_complete callback failed"
	
	/* Parsing-related errors */
	INVALID_EOF_STATE ,  // "stream ended at an unexpected time"
	HEADER_OVERFLOW ,  // "too many header bytes seen; overflow detected"
	CLOSED_CONNECTION ,  // "data received after completed connection: close message"
	INVALID_VERSION , // "invalid HTTP version"
	INVALID_STATUS , // "invalid HTTP status code"
	INVALID_METHOD , // "invalid HTTP method"
	INVALID_URL , // "invalid URL"
	INVALID_HOST , // "invalid host"
	INVALID_PORT , // "invalid port"
	INVALID_PATH , // "invalid path"
	INVALID_QUERY_STRING , // "invalid query string"
	INVALID_FRAGMENT ,  //"invalid fragment"
	LF_EXPECTED ,  // "LF character expected"
	INVALID_HEADER_TOKEN , // "invalid character in header"
	INVALID_CONTENT_LENGTH ,  // "invalid character in content-length header"
	INVALID_CHUNK_SIZE , // "invalid character in chunk size header" 
	INVALID_CONSTANT , // "invalid constant string"
	INVALID_INTERNAL_STATE , // "encountered unexpected internal state"
	STRICT , // "strict mode assertion failed"
	PAUSED , // "parser is paused"
	UNKNOWN , // "an unknown error occurred"
};

struct http_parser {
	/*
	unsigned char type : 2;
	unsigned char flags : 6;
	*/
	uv_uint8 __bit;

	/** PRIVATE **/
	uv_uint8 state;        /* enum state from http_parser.c */
	uv_uint8 header_state; /* enum header_state from http_parser.c */
	uv_uint8 index;        /* index into current matcher */

	uv_uint32 nread;          /* # bytes read in various scenarios */
	uv_uint64 content_length; /* # bytes in body (0 if no Content-Length header) */

	/** READ-ONLY **/
	uv_uint16 http_major;
	uv_uint16 http_minor;
	uv_uint16 status_code; /* responses only */
	uv_uint8 method;       /* requests only */
	
	/*
	ubyte http_errno : 7;
	ubyte upgrade : 1;
	*/
	uv_uint8	__bit2;
	
	/** PUBLIC **/
	void* data;
	static assert(http_parser.sizeof is 32 );
}

struct http_parser_settings {
	http_cb      on_message_begin;
	http_data_cb on_url;
	http_cb      on_status_complete;
	http_data_cb on_header_field;
	http_data_cb on_header_value;
	http_cb      on_headers_complete;
	http_data_cb on_body;
	http_cb      on_message_complete;
	static assert(http_parser_settings.sizeof is 32 );
}

enum http_parser_url_fields{
	UF_SCHEMA           = 0 ,
	UF_HOST             = 1 ,
	UF_PORT             = 2 ,
	UF_PATH             = 3 ,
	UF_QUERY            = 4 ,
	UF_FRAGMENT         = 5 ,
	UF_USERINFO         = 6 ,
	UF_MAX              = 7 ,
};

struct http_parser_url {
	uv_uint16 field_set;           /* Bitmask of (1 << UF_*) values */
	uv_uint16 port;                /* Converted UF_PORT string */
	static  struct field_data_s {
		uv_uint16 off;               /* Offset into buffer in which field starts */
		uv_uint16 len;               /* Length of run in buffer */
	}
	field_data_s[http_parser_url_fields.UF_MAX]  field_data;
};

void http_parser_init(http_parser *parser, http_parser_type type);
size_t http_parser_execute(http_parser* parser,  const(http_parser_settings*) settings, uv_charptr data, size_t len);
uv_int32 http_should_keep_alive(const(http_parser*) parser);
uv_charptr http_method_str(http_method m);
uv_charptr http_errno_name(http_errno err);
uv_charptr http_errno_description(http_errno err);
int http_parser_parse_url(uv_charptr buf, size_t buflen, uv_int32 is_connect, http_parser_url *u);
void http_parser_pause(http_parser* parser, uv_int32 paused);
uv_int32 http_body_is_final(const(http_parser*) parser);

version(HTTPPARSER_TEST){
	
	import tango.io.Stdout;
	
	extern(D) :
	
	
	struct Node {
		char[]	name ;
		
		http_cb		on_message_begin	= &_message_begin ;
		http_data_cb	on_url			= &_url ;
		http_cb		on_status_complete	= &_status_complete ;
		http_data_cb	on_header_field		= &_header_field ;
		http_data_cb	on_header_value		= &_header_value ;
		http_cb		on_headers_complete	= &_headers_complete ;
		http_data_cb	on_body			= &_body ;
		http_cb		on_message_complete	= &_message_complete ;
		
		http_parser		hp ;

		http_parser_settings* hps(){
			return cast(http_parser_settings*) ( (cast(uint) &this) + on_message_begin.offsetof );
		}
		
		static Node* Init( http_parser_type type){
			Node* p	= new Node;
			http_parser_init(&p.hp,  type);
			p.hp.data	= p ;
			return p ;
		}
		
		static extern(C) int _message_begin(http_parser* _hp){
			Stdout.formatln("{}: message_begin", __LINE__ );
			return 0 ;
		}
		
		static extern(C) int _status_complete(http_parser* _hp){
			Stdout.formatln("{}: status_complete", __LINE__ );
			return 0 ;
		}
		
		static extern(C) int _url(http_parser* _hp, char* da, size_t len){
			Stdout.formatln("{}: url = `{}`", __LINE__ , da[0..len]);
			return 0 ;
		}
		
		static extern(C) int _fragment(http_parser* _hp, char* da, size_t len){
			Stdout.formatln("{}: fragment = `{}`", __LINE__ , da[0..len]);
			return 0 ;
		}
		
		static extern(C) int _header_field(http_parser* _hp, char* da, size_t len){
			Stdout.formatln("{}: header_field = `{}`", __LINE__ , da[0..len]);
			return 0 ;
		}
		
		static extern(C) int _header_value(http_parser* _hp, char* da, size_t len){
			Stdout.formatln("{}: header_value = `{}`", __LINE__ , da[0..len]);
			return 0 ;
		}
		
		static extern(C) int _body(http_parser* _hp, char* da, size_t len){
			Stdout.formatln("{}: body = `{}`", __LINE__ , da[0..len]);
			return 0 ;
		}
		
		static extern(C) int _headers_complete(http_parser* _hp){
			Stdout.formatln("{}: headers_complete", __LINE__ );
			return 0 ;
		}
		
		static extern(C) int _message_complete(http_parser* _hp){
			Stdout.formatln("{}: message_complete", __LINE__ );
			return 0 ;
		}
	}
	
	auto da	= "POST /post_identity_body_world?q=search#hey HTTP/1.1\r\n"
         "Accept: */*\r\n"
         "Transfer-Encoding: identity\r\n"
         "Content-Length: 6\r\n"
         "\r\n"
         " World" ;
	
	void main(){
		
		Node* n1	= Node.Init( http_parser_type. HTTP_REQUEST ) ;
		auto hps	= n1.hps ;
		
		Stdout.formatln("on_message_begin:{} = {} ", hps.on_message_begin, &Node._message_begin );
		Stdout.formatln("on_url:{}", hps.on_url );
		Stdout.formatln("on_header_field:{}", hps.on_header_field );
		
		
		Stdout.formatln("on_header_value:{}", hps.on_header_value );
		Stdout.formatln("on_headers_complete:{}", hps.on_headers_complete );
		Stdout.formatln("on_body:{}", hps.on_body );
		Stdout.formatln("on_message_complete:{}", hps.on_message_complete );
		
		
		Stdout.formatln("\nhttp_parser:{}", &n1.hp);
		Stdout.formatln("http_parser_settings:{}\n", n1.hps).flush;
		
		int off	= da.length /19* 18 ;
		auto d1	= da[0..off];
		auto d2	= da[off..$];
		int i	= http_parser_execute( &n1.hp, n1.hps, cast(char*) &d1[0], d1.length  );
		
		Stdout.formatln("`{}`", d2);
		assert(i is d1.length);
		Stdout.formatln("{}: stat = `{}:{}`\n", __LINE__, n1.hp.state , n1.hp.state );
		
		i	= http_parser_execute(&n1.hp, n1.hps, cast(char*)  &d2[0], d2.length  );
		assert(i is d2.length);
		Stdout.formatln("{}: stat = `{}:{}`", __LINE__,  n1.hp.state, n1.hp.state );
		Stdout.formatln("{}: header_state = `{}:{}`", __LINE__, n1.hp.header_state, n1.hp.header_state );
	}
}